package org.despair2D.ui.core 
{
	import flash.display.DisplayObject;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import org.despair2D.core.ProcessManager;
	import org.despair2D.core.ns_despair;
	import org.despair2D.debug.Logger;
	import org.despair2D.renderer.IView;
	import org.despair2D.ui.DespairUI;
	import org.despair2D.ui.Fusion;
	import org.despair2D.ui.events.ManipulateEvent;
	use namespace ns_despair;
	
	[Event(name = "press", type = "org.despair2D.ui.events.ManipulateEvent")] 
	
	[Event(name = "release", type = "org.despair2D.ui.events.ManipulateEvent")] 
	
	[Event(name = "over", type = "org.despair2D.ui.events.ManipulateEvent")] 
	
	[Event(name = "leave", type = "org.despair2D.ui.events.ManipulateEvent")] 
	
	[Event(name = "move", type = "org.despair2D.ui.events.ManipulateEvent")] 
	
	[Event(name = "click", type = "org.despair2D.ui.events.ManipulateEvent")] 
	
	[Event(name = "startDrag", type = "org.despair2D.ui.events.ManipulateEvent")]
	
	[Event(name = "dragging", type = "org.despair2D.ui.events.ManipulateEvent")]
	
	[Event(name = "stopDrag", type = "org.despair2D.ui.events.ManipulateEvent")]
	
	[Event(name = "killed", type = "org.despair2D.ui.events.ManipulateEvent")] 
	
	[Event(name = "interactiveChange", type = "org.despair2D.ui.events.InteractiveEvent")] 
	
	[Event(name = "visibleChange", type = "org.despair2D.ui.events.VisibleEvent")] 
	
	/** @inheritDoc **/
public class ComponentProxy implements IComponent
{
	
	//======================
	// View Property
	//======================
	
	
	final public function get name() : String { return view.name; }
	final public function set name( v:String ) : void { view.name = v; }
	
	final public function get mouseX() : Number { return view.mouseX; }
	final public function get mouseY() : Number { return view.mouseY; }
	
	final public function get visible () : Boolean { return view.visible; }
	final public function set visible ( b:Boolean ) : void { view.visible = b; }
	
	public function get x () : Number{ return view.x; }
	public function set x ( v:Number ) : void { view.x = v; }
	
	public function get y () : Number{ return view.y; }
	public function set y ( v:Number ) : void { view.y = v; }

	public function get width() : Number { return view.width }
	public function get height () : Number { return view.height }
	
	public function get rotation () : Number{ return view.rotation; }
	public function set rotation ( v:Number ) : void { view.rotation = v; }
	
	public function get scaleX () : Number { return view.scaleX; }
	public function set scaleX ( v:Number ) : void { view.scaleX = v; }
	
	public function get scaleY () : Number { return view.scaleY; }
	public function set scaleY ( v:Number ) : void { view.scaleY = v; }
	
	public function get alpha () : Number { return view.alpha; }
	public function set alpha ( v:Number ) : void { view.alpha = v; }
	
	public function get filters () : Array { return view.filters; }
	public function set filters ( v:Array ) : void { view.filters = v; }
	
	public function get interactive () : Boolean { return view.interactive; }
	public function set interactive ( b:Boolean ) : void { view.interactive = b; }

	
	public function move( x:Number, y:Number ) : void
	{
		view.move(x, y);
	}
	
	public function offset( tx:Number, ty:Number ) : void
	{
		view.offset(tx, ty);
	}
	
	public function moveInStage( gx:Number, gy:Number ) : void
	{
		(view as Component).moveInStage(gx, gy)
	}
	
	public function reset() : void
	{
		view.reset()
	}
	
	final public function addEventListener( type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false ) : void
	{
		view.addEventListener(type, listener, useCapture, priority, useWeakReference);
	}
	
	final public function removeEventListener( type:String, listener:Function, useCapture:Boolean = false ) : void
	{
		view.removeEventListener(type, listener, useCapture);
	}
	
	final public function dispatchEvent( event:Event ) : Boolean
	{
		return view.dispatchEvent(event);
	}
	
	final public function hasEventListener( type:String ) : Boolean
	{
		return view.hasEventListener(type);
	}
	
	final public function willTrigger( type:String ) : Boolean
	{
		return view.willTrigger(type);
	}
	
	
	//======================
	// Component Property
	//======================
	
	public function get displayObject() : DisplayObject { return view as DisplayObject }
	
	public function get globalPos() : Point { return (view as Component).globalPos }
	final public function get layer() : int { return (view as Component).layer }
	final public function get fusion() : Fusion { return m_parent ? m_parent.m_proxy as Fusion : null }
	
	final public function get hovered() : Boolean
	{
		var c:IComponent = UIManager.hoveredElement
		return c && c.bePartOf(this)
	}
	
	final public function get pressed() : Boolean
	{
		var c:IComponent = DespairUI.pressedElement
		return c && c.bePartOf(this)
	}
	
	final public function get dragging() : Boolean 
	{ 
		return m_startDragging
	}
	
	final public function get draggingInBounds() : Boolean
	{ 
		return m_startDragging && m_draggingInBounds
	}
	
	public function get spaceWidth() : Number { return (view as Component).spaceWidth }
	public function set spaceWidth( v:Number ) : void { (view as Component).spaceWidth = v }
	
	public function get spaceHeight() : Number { return (view as Component).spaceHeight }
	public function set spaceHeight( v:Number ) : void { (view as Component).spaceHeight = v }
	
	
	final public function bePartOf( c:IComponent ) : Boolean
	{
		return (view as Component).bePartOf(c)
	}
	
	public function kill() : void
	{
		if (m_parent)
		{
			m_parent.removeElement(this);
		}
		this.dispose();
	}	
	
	
	ns_despair function get view() : IView
	{
		return null
	}
	
	ns_despair function get shell() : DisplayObject
	{
		return null
	}
	
	ns_despair function dispose() : void
	{
		if (m_startDragging)
		{
			this.stopDrag()
		}
		(view as Object).dispose()
		m_parent = null
	}
	
	
	ns_despair static var m_stage:Stage
	
	ns_despair static var m_draggingCompList:Array = []
	
	ns_despair static var m_numDragging:int
	
	ns_despair var m_parent:FusionComp
	
	ns_despair var m_startDragging:Boolean, m_draggingInBounds:Boolean

	ns_despair var m_boundsX:Number, m_boundsY:Number, m_boundsWidth:Number, m_boundsHeight:Number, m_draggingOffsetX:Number, m_draggingOffsetY:Number
	
	
	/**
	 * 开始拖动
	 * @param	lockCenter
	 * @param	bounds		该范围相对于stage
	 * 
	 * @usage	stage尺寸改变需要滑鼠按下拖动，与拖动组件不会同时发生，所以最大拖动范围直接锁定当前舞台尺寸.
	 */
	final public function startDrag( lockCenter:Boolean = false, bounds:Rectangle = null ) : void
	{
		var stageWidth:Number, stageHeight:Number
		var gp:Point
		
		stageWidth   =  m_stage.stageWidth
		stageHeight  =  m_stage.stageHeight
		gp           =  this.globalPos
		
		if (lockCenter)
		{
			m_draggingOffsetX = m_draggingOffsetY = 0
			this.moveInStage(m_stage.mouseX, m_stage.mouseY)
		}
		
		else
		{
			m_draggingOffsetX  =  m_stage.mouseX - gp.x;
			m_draggingOffsetY  =  m_stage.mouseY - gp.y;
		}

		if (bounds)
		{
			if (bounds.x < 0)
			{
				m_boundsX  =  0
			}
			
			else if (bounds.x < stageWidth)
			{
				m_boundsX = bounds.x
			}
			
			else
			{
				Logger.reportError(this, 'startDrag', '拖动参数bounds(x)有误 !!')
			}
			
			if (bounds.y < 0)
			{
				m_boundsY  =  0
			}
			
			else if (bounds.y < stageHeight)
			{
				m_boundsY = bounds.y
			}
			
			else
			{
				Logger.reportError(this, 'startDrag', '拖动参数bounds(y)有误 !!')
			}
			
			if (bounds.right > stageWidth)
			{
				m_boundsWidth = stageWidth - bounds.x
			}
			
			else
			{
				m_boundsWidth = bounds.width
			}
			
			if (bounds.bottom > stageHeight)
			{
				m_boundsHeight = stageHeight - bounds.y
			}
			
			else
			{
				m_boundsHeight = bounds.height
			}
		}
		
		else
		{
			m_boundsX       =  -Infinity
			m_boundsY       =  -Infinity
			m_boundsWidth   =  Infinity
			m_boundsHeight  =  Infinity
		}
		
		whetherInBoundsForDrag(m_stage.mouseX, m_stage.mouseY)
		
		if (!m_startDragging)
		{
			if(m_numDragging == 0)
			{
				m_stage.addEventListener(MouseEvent.MOUSE_MOVE, ____onDragging)
			}
			
			m_draggingCompList[m_numDragging++] = this
			m_startDragging = true
			this.dispatchEvent(new ManipulateEvent(ManipulateEvent.START_DRAG))
		}
	}
	
	final public function stopDrag( fireEvent:Boolean = false ) : void
	{
		var index:int
		
		index = m_draggingCompList.indexOf(this)
		m_draggingCompList[index] = m_draggingCompList[--m_numDragging];
		m_draggingCompList.pop()
		
		if (m_numDragging == 0)
		{
			m_stage.removeEventListener(MouseEvent.MOUSE_MOVE, ____onDragging)
		}
		m_startDragging = false
		if (fireEvent)
		{
			this.dispatchEvent(new ManipulateEvent(ManipulateEvent.STOP_DRAG))
		}
	}
	
	ns_despair static function stopAllDrag( fireEvent:Boolean ) : void
	{
		var l:int
		var c:ComponentProxy
		
		if (m_numDragging > 0)
		{
			l = m_numDragging
			while (--l > -1)
			{
				c = m_draggingCompList[l]
				c.m_startDragging = false
				if (fireEvent)
				{
					c.dispatchEvent(new ManipulateEvent(ManipulateEvent.STOP_DRAG))
				}
			}
			m_draggingCompList.length = m_numDragging = 0
			m_stage.removeEventListener(MouseEvent.MOUSE_MOVE, ____onDragging)
		}
	}
	
	/**
	 * 统一处理全部拖动行为
	 */
	ns_despair static function ____onDragging( e:MouseEvent ) : void
	{
		var mouseX:Number, mouseY:Number
		var c:ComponentProxy
		var l:int
		
		mouseX  =  m_stage.mouseX
		mouseY  =  m_stage.mouseY
		l       =  m_numDragging
		
		while (--l > -1)
		{
			c = m_draggingCompList[l]
			c.whetherInBoundsForDrag(mouseX, mouseY)
			
			if (c.m_draggingInBounds)
			{
				c.moveInStage(mouseX - c.m_draggingOffsetX, mouseY - c.m_draggingOffsetY)
			}
			c.dispatchEvent(new ManipulateEvent(ManipulateEvent.DRAGGING))
		}
		
		// 渲染(强制进入下一帧)
		e.updateAfterEvent()
		
		// 执行更新
		ProcessManager.updateAll()
	}
	
	ns_despair function whetherInBoundsForDrag( mouseX:Number, mouseY:Number ) : void
	{
		m_draggingInBounds = !Boolean(mouseX < m_boundsX || mouseX > m_boundsX + m_boundsWidth || mouseY < m_boundsY || mouseY > m_boundsY + m_boundsHeight)
	}
}
}